package com.frinder.base.client;

import com.frinder.base.MarshallingCodeCFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName AbstractClient
 * @Author frinder
 * @Description 与下位机交互基类
 * @Date 2019/1/16 16:50
 * @Version 1.0
 */
@Slf4j
public abstract class AbstractClient {

    private final EventLoopGroup workerGroup = new NioEventLoopGroup();

    private Channel channel;

    @Getter
    private final String name;
    @Getter
    private final String host;
    @Getter
    private final int port;

    public AbstractClient(String name, String host, int port) {
        this.name = name;
        this.host = host;
        this.port = port;
    }

    /**
     * @Author frinder
     * @Description 获取监控“狗”
     * @Date 2018/11/29 14:06
     * @Param []
     * @Return com.frinder.base.netty.client.ConnectionWatchDog
     */
    protected ConnectionWatchDog getConnectionWatchDog() {
        return new ConnectionWatchDog(this, true) {
            @Override
            public ChannelHandler[] handlers() {
                return new ChannelHandler[]{
                        // 重连机制
                        this,
                        // 日志处理器
                        new LoggingHandler(LogLevel.DEBUG),
                        // 解码&编码 机制，需要放在最前面，后面所有过滤器都需要先进行 编码&解码
                        MarshallingCodeCFactory.decoder(),
                        MarshallingCodeCFactory.encoder(),
                        // 消息过滤器
                        new ClientMessageAdapter(),
                        // 消息处理机制
                };
            }
        };
    }

    /**
     * 启动客户端
     */
    public void start() {
        log.info("*** {} 开始连接服务端！", name);
        try {
            final ConnectionWatchDog watchDog = getConnectionWatchDog();
            synchronized (watchDog) {
                ChannelFuture future;
                Bootstrap bootstrap = watchDog.getBootstrap();
                bootstrap.group(workerGroup)
                        .channel(NioSocketChannel.class)
                        .option(ChannelOption.SO_KEEPALIVE, true)
                        .handler(new ChannelInitializer<Channel>() {
                            @Override
                            protected void initChannel(Channel channel) throws Exception {
                                channel.pipeline().addLast(watchDog.handlers());
                            }
                        });
                future = bootstrap.connect(host, port).sync();
                channel = future.channel();
            }
            log.info("*** {} 成功连接服务端！", name);
        } catch (Exception e) {
            log.error("*** {} 连接服务端失败，原因：{}", name, e.getMessage());
            workerGroup.shutdownGracefully();
        }
    }

    /**
     * shutdown
     */
    public void destroy() {
        workerGroup.shutdownGracefully();
    }

    /**
     * 发送消息
     *
     * @param request
     */
    public void send(Object request) {
        if (null == channel) {
            throw new NullPointerException("渠道为空，异常！");
        }
        log.info("*** 机器人：{} 发送消息：{}", name, request);
        channel.writeAndFlush(request);
    }

    /**
     * 重连后重新设置 channel
     *
     * @param channel
     */
    public void resetChannel(Channel channel) {
        try {
            this.channel.closeFuture().sync();
            this.channel.close().sync();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        this.channel = channel;
    }

}
